"""
Copyright (C) 2008 Masahiro Nakao

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
"""

import math
import sys
import copy
import numpy
from gene_rc import Gene_rc
import functions
from parameter import Parameter
from random_org import Random_org

def show_result(generation, gene, number_of_evaluations):
    print "Generation:",
    print generation,
    print ", Evaluations:",
    print number_of_evaluations,
    print ", Best Value:",
    print gene.get_value(),
    print ", Design Variables:",
    for i in range(len(gene.get_chrom())):
        if i != len(gene.get_chrom()) - 1:
            print gene.get_chrom()[i],
        else:
            print gene.get_chrom()[i]

def judge_termination(min, number_of_evaluations, pm):
    minimum_value = pm.get_minimum_value()
    max_number_of_evaluations = pm.get_max_number_of_evaluations()
    value = 0
        
    if number_of_evaluations > max_number_of_evaluations:
        print "Reached the maximum number of evaluation"
        value = 1
    
    if minimum_value > min:
        print "Find minimum value"
        value = 1
        
    return value

#layer and generation ha itsuka kesu#
def generate_offspring_each_layer(q, var, pm, r, layer, generation):
    offspring = []
    design_variable = []
    num = pm.get_number_of_each_individuals()
    max_value = pm.get_max_value()
    min_value = pm.get_min_value()

    for i in q:
        design_variable.append(i.get_chrom())
    
    dimension = len(design_variable[0])
    
    reverse_design_variable = []
    for i in range(dimension):
        tmp = []
        for j in range(len(design_variable)):
            tmp.append(design_variable[j][i])
        reverse_design_variable.append(tmp)
    
    average = [] # average[dimension]
    for i in range(dimension):
        average.append(numpy.average(reverse_design_variable[i]))

## ADD for searching normalvariate value
    amp = pm.get_amp()
    print "Nor info:",
    print "Gen=" + str(generation),
    print "Layer=" + str(layer),  
    for i in range(dimension):
        print "x" + str(i) + "=" + str(average[i]),
    print "R=",
    for i in range(dimension):
        print str(math.sqrt(float(var[i]))*amp),
    print ""
## END ADD

    for i in range(num):
        tmp = []
        for j in range(dimension):
            k = 0
            
            while True:
                value = r.normalvariate(average[j], math.sqrt(float(var[j]))*amp)
#                value = r.uniform(average[j]-float(var[j])/2,average[j]+float(var[j])/2)
    
                if value >= min_value and value <= max_value:
                    break
                elif k > 100:
                    print "normalvariate error!!"
                    if r.get_random() < 0.5:
                        value = min_value
                    else:
                        value = max_value
                    break
                k += 1
                    
            tmp.append(value)
        offspring.append(tmp)
    
    return offspring

def generate_offspring(q, var, pm, r, generation):
    number_of_layers = pm.get_number_of_layers()
    
    offspring = []
    for layer in range(number_of_layers):
        offspring.append(generate_offspring_each_layer(q[layer], var[layer], pm, r, layer, generation))
    
    return offspring

def get_range_of_each_layer(pm):
## d ha tousa you
#    d = (float(pm.get_min_range()) - pm.get_max_range())/(float(pm.get_number_of_layers()) - 1)
    a = []
    for i in range(pm.get_number_of_layers()):
##      touhi
        a.append(pm.get_max_range() * ((float(pm.get_min_range())/pm.get_max_range()) ** (float(i) /(pm.get_number_of_layers() - 1))))
##      tousa
#         a.append(float(pm.get_max_range()) + d * i)

    return a

def show_layers_information(number_of_layers, number_of_samples_for_estimation):
    
    print "----------------------------------------"
    
    for i in range(number_of_layers - 1, -1, -1):
        print "Layer = " + str(i) + "\tnumber_of_sample = " + str(number_of_samples_for_estimation[i])
        
    print "----------------------------------------"

def get_number_of_samples_for_estimation(pm, number_of_total_individuals):
    number_of_samples_each_layer = [number_of_total_individuals] # The first layer

    for i in range(1, pm.get_number_of_layers() - 1):
        tmp = number_of_total_individuals - (float(number_of_total_individuals) - 1)/(pm.get_number_of_layers() - 1) * i
        number_of_samples_each_layer.append(int(tmp))

    number_of_samples_each_layer.append(1) # The end layer
    
    return number_of_samples_each_layer

def set_individuals_each_layers(pm, r):
    population = []
        
    for i in range(pm.get_number_of_layers()):
        gene = []
        for j in range(pm.get_number_of_each_individuals()):
            # Create Gene
            gene.append(Gene_rc(99999))
            gene[j].init_chrom(r, pm.get_number_of_dimensions(), pm.get_max_value(), pm.get_min_value())
            gene[j].set_value(functions.evaluation(pm.get_function(), gene[j].get_chrom()))
            
        # population[layer][sample_number]
        population.append(gene)
            
    return population

def selection_each_layer(pm, population, number_of_samples_for_estimation):
    tmp_gene = []
    k = 0
    
    for i in range(pm.get_number_of_layers()):
        for j in range(pm.get_number_of_each_individuals()):
            tmp_gene.append(Gene_rc(99999))
            tmp_gene[k].set_chrom(population[i][j].get_chrom())
            tmp_gene[k].set_value(population[i][j].get_value())
            k += 1
        
    tmp_gene.sort()
            
    q = []
    # number_of_samples_for_estimation[layer]
    for i in number_of_samples_for_estimation:
        q.append(tmp_gene[:i])
                
    return q

def replace_population(pm, offspring):
    population = []
    
    for i in range(pm.get_number_of_layers()):
        gene = []
        for j in range(pm.get_number_of_each_individuals()):
            gene.append(Gene_rc(99999))
            gene[j].set_chrom(offspring[i][j])
            gene[j].set_value(functions.evaluation(pm.get_function(), gene[j].get_chrom()))
        
        # population[layer][sample_number]
        population.append(gene)
    
    return population

def get_value(pm, population):
    value = []
    
    for i in range(pm.get_number_of_layers()):
        for j in range(pm.get_number_of_each_individuals()):
            value.append(population[i][j].get_value())
    
    return value

def get_best_gene(pm, population):
    tmp_gene = []
    k = 0
    
    for i in range(pm.get_number_of_layers()):
        for j in range(pm.get_number_of_each_individuals()):
            tmp_gene.append(Gene_rc(99999))
            tmp_gene[k].set_chrom(population[i][j].get_chrom())
            tmp_gene[k].set_value(population[i][j].get_value())
            k += 1
        
    tmp_gene.sort()
    
    return tmp_gene[0]

def show_allresult(population, generation, number_of_evaluations):
    print ""
    print "Number of Evaluations : " + str(number_of_evaluations)
    
    print "Generations Layer Value\t:\tChrom"
    for layer, population_of_each_layers in enumerate(population):
        for individual in population_of_each_layers:
            print str(generation) + "\t" + str(layer) + "\t" + str(individual.get_value()) + " : ",
            for chrom in individual.get_chrom():
                print chrom,
            print ""

def calc_each_range(selected_genes):
    tmp = []
    for gene in selected_genes:
        tmp.append(gene.get_chrom())
        
    value = numpy.array(tmp)
    
    return value.var(axis=0)

def main():
    # Set Parameter
    pm = Parameter(sys.argv)
    
    # Create Ramdom Class
    r = Random_org(pm.get_seed())
    
    # Show Parameter
    pm.show_parameter()
    
    # Initialization
    number_of_evaluations = 0
    best_result = 99999 # Variable for comparison
    number_of_total_individuals = pm.get_number_of_layers() * pm.get_number_of_each_individuals()
    
    # number_of_samples_for_estimation[layer]
    number_of_samples_for_estimation = get_number_of_samples_for_estimation(pm, number_of_total_individuals)
    
    # show layers imformation
    show_layers_information(pm.get_number_of_layers(), number_of_samples_for_estimation)
    
    # population[layer][sample_number] : Get individuals ready
    population = set_individuals_each_layers(pm, r)
    number_of_evaluations = number_of_total_individuals
    
    # Start Optimization
    for generation in range(1, pm.get_max_number_of_generations()):
        
        # show result
        show_allresult(population, generation, number_of_evaluations)
        
        # Selection Operator in each layer's population : selected_genes[layer]
        selected_genes = selection_each_layer(pm, population, number_of_samples_for_estimation)
        
        # Calc var of each layers : offspring_range[layer]
        tmp_range = []
        for layer in range(pm.get_number_of_layers()-1):
            tmp_range.append(calc_each_range(selected_genes[layer]))
        
        offspring_range = []
        for layer in range(pm.get_number_of_layers()-1):
            each_range = []
            for n in range(pm.get_number_of_dimensions()):
                each_range.append(tmp_range[layer][n])
            
            offspring_range.append(each_range)
            
        offspring_range.append([pm.get_min_range()**2] * pm.get_number_of_dimensions())
        
        # Generate sample : offspring[layer][individual][dimension]
        offspring = generate_offspring(selected_genes, offspring_range, pm, r, generation)
        
        # Replace Population
        population = replace_population(pm, offspring)
        number_of_evaluations += number_of_total_individuals
        
        # Get Best Gene
        best_gene = get_best_gene(pm, population)
        
        # Value of Population
        #value = get_value(pm, population)
        
        # Output results
        show_result(generation, best_gene, number_of_evaluations)
        
        # exist best?
        if best_result > best_gene.get_value():
            best_result = best_gene.get_value()
        
        # Terminate
        if judge_termination(best_gene.get_value(), number_of_evaluations, pm):
            break
        
    # End of Generation

# Main
if __name__ == '__main__':
    main()